package src.NPCs;

import src.Util.*;
import src.Main;
import src.Players.*;

public class NPCUpdate
{
    public void updateNPC(Player p, Stream str)
    {
        if(p != null && str != null)
        {
            Main.m.ge.globalStream.resetOffset();
            str.createFrameVarSizeWord(65);
            str.initBitAccess();
            str.writeBits(8, p.npcListSize);
            int size = p.npcListSize;
            p.npcListSize = 0;
            for(int i = 0; i < size; i++)
            {
                if(p.npcList[i] != null)
                {
                    if(!p.rebuildNPCList && withinDistance(p, p.npcList[i]))
                    {
                        Main.m.ge.moveN.updateNPCMovement(p.npcList[i], str);
                        appendNPCUpdateBlock(p.npcList[i], Main.m.ge.globalStream);
                        p.npcList[p.npcListSize++] = p.npcList[i];
                    }
                    else
                    {
                        int id = p.npcList[i].npcIndex;
                        p.npcInListBitmap[id >> 3] &= ~(1 << (id & 7));
                        str.writeBits(1, 1);
                        str.writeBits(2, 3);
                    }
                }
            }
            for(NPC n : Main.m.ge.npcs)
            {
                if(n != null)
                {
                    int id = n.npcIndex;
                    if((p.npcInListBitmap[id >> 3] & (1 << (id & 7))) == 0)
                    {
                        if(withinDistance(p, n))
                        {
                            addNewNPC(p, n, str);
                        }
                    }
                }
            }
            p.rebuildNPCList = false;
            if(Main.m.ge.globalStream.getOffset() > 0)
            {
                str.writeBits(14, 16383);
                str.finishBitAccess();
                str.writeBytes(Main.m.ge.globalStream.getBuffer(), Main.m.ge.globalStream.getOffset(), 0);
            }
            else
            {
                str.finishBitAccess();
            }
            str.endFrameVarSizeWord();
        }
    }

    public void appendNPCUpdateBlock(NPC n, Stream str)
    {
        if(str != null && n != null)
        {
            if(!n.updateRequired)
            {
                return;
            }
            NPCUpdateBlock ub = new NPCUpdateBlock();
            int updateMask = 0;
            if(n.animUpdateRequired)
            {
                updateMask |= 0x10;
            }
            if(n.gfxUpdateRequired)
            {
                updateMask |= 0x80;
            }
            if(n.speakTextUpdateRequired)
            {
                updateMask |= 1;
            }
            if(n.hit1UpdateRequired)
            {
                updateMask |= 0x40;
            }
            if(n.faceCoordsUpdateRequired)
            {
                updateMask |= 4;
            }
            ub.writeMask(str, updateMask);
            if(n.animUpdateRequired)
            {
                ub.appendAnimationRequest(n, str);
            }
            if(n.gfxUpdateRequired)
            {
                ub.appendGraphicsRequest(n, str);
            }
            if(n.speakTextUpdateRequired)
            {
                ub.appendNPCSpeakText(n, str);
            }
            if(n.hit1UpdateRequired)
            {
                ub.appendHit1(n, str);
            }
            if(n.faceCoordsUpdateRequired)
            {
                ub.appendFaceCoords(n, str);
            }
            ub = null;
        }
    }

    private boolean withinDistance(Player p, NPC npc)
    {
        if(npc != null && p != null)
        {
            if(p.heightLevel != npc.heightLevel || npc.hiddenNPC)
            {
                return false;
            }
            int deltaX = npc.absX - p.absX, deltaY = npc.absY - p.absY;
            return (deltaX <= 15 && deltaX >= -16 && deltaY <= 15 && deltaY >= -16);
        }
        return false;
    }

    public void clearUpdateFlags(NPC n)
    {
        if(n != null)
        {
            n.updateRequired = false;
            n.speakTextUpdateRequired = false;
            n.animUpdateRequired = false;
            n.gfxUpdateRequired = false;
            n.faceCoordsUpdateRequired = false;
            n.hit1UpdateRequired = false;
            n.speakText = "";
            n.animRequest = 65535;
            n.animDelay = 0;
            n.gfxRequest = 65535;
            n.gfxDelay = 0;
            n.moveX = n.moveY = 0;
            n.direction = -1;
            n.faceCoordsX = -1;
            n.faceCoordsY = -1;
            n.hitDiff1 = 0;
            n.posionHit1 = 0;
        }
    }

    public void addNewNPC(Player p, NPC npc, Stream str)
    {
        if(npc != null && str != null && p != null)
        {
            int id = npc.npcIndex;
            p.npcInListBitmap[id >> 3] |= 1 << (id & 7);
            p.npcList[p.npcListSize++] = npc;
            str.writeBits(14, id);
            int z = npc.absY - p.absY;
            if(z < 0)
            {
                z += 32;
            }
            str.writeBits(5, z);
            z = npc.absX - p.absX;
            if(z < 0)
            {
                z += 32;
            }
            str.writeBits(5, z);
            str.writeBits(1, 0);
            str.writeBits(12, npc.npcType);
            boolean savedUpdateRequired = npc.updateRequired;
            npc.updateRequired = true;
            appendNPCUpdateBlock(npc, Main.m.ge.globalStream);
            npc.updateRequired = savedUpdateRequired;	
            str.writeBits(1, 1);
        }
    }
}